#pragma once
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include<unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

#define MAX_LISTEN 1

class Socket
{
private:
    bool Create()
    {
        // int socket(int domain, int type, int protocol);
        _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (_sockfd < 0)
        {
            logMessage(ERROR, "socket error, %d:%s", errno, strerror(errno));
            return false;
        }
        return true;
    }
    
    bool Bind(const std::string& ip, uint16_t port)
    {
        //  int bind(int socket, const struct sockaddr *address, socklen_t address_len);
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        if (bind(_sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
        {
            logMessage(ERROR, "bind error, %d:%s", errno, strerror(errno));
            return false;
        }
        return true;
    }

    bool Listen(int backlog = MAX_LISTEN)
    {     
        // int listen(int sockfd, int backlog);
        if(listen(_sockfd, backlog) < 0)
        {
            logMessage(ERROR, "listen error, %d:%s", errno, strerror(errno));
            return false;
        }
        return true;
    }

    bool Connect(const std::string& ip, uint16_t port)
    {
        // int connect(int socket, const struct sockaddr *address, socklen_t address_len);
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        if (connect(_sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
        {
            logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno)); 
            return false;
        }
        return true;
    }

    void NonBlock()
    {
        // int fcntl(int fd, int cmd, ... /*arg*/);
        int flag = fcntl(_sockfd, F_GETFL, 0);
        fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
    }

    void ReuseAddr()
    {
        // int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
        int optval = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(int)); 
        optval = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void*)&optval, sizeof(int)); 
    }

public:
    Socket(int sockfd = -1) : _sockfd(sockfd) {}

    int GetFd() { return _sockfd; }

    int Accept()
    {
        // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        int servicesock = accept(_sockfd, nullptr, nullptr);
        if(servicesock < 0)
        {
            logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
            return -1;
        }
        return servicesock;
    }

    ssize_t Recv(void* buf, size_t len, int flag = 0)
    {
        // ssize_t recv(int sockfd, void *buf, size_t len, int flags);
        ssize_t s = recv(_sockfd, buf, len, flag);
        if(s <= 0)
        {
            if(errno == EAGAIN || errno == EINTR) // 无数据(非阻塞) || 阻塞等待被连接打断
                return 0;
            logMessage(ERROR, "recv error, %d:%s", errno, strerror(errno)); 
            return -1;
        }
        return s;
    }

    ssize_t RecvNonBlock(void* buf, size_t len) { return Recv(buf, len, MSG_DONTWAIT); }

    ssize_t Send(const void* buf, size_t len, int flag = 0)
    {
        if(len == 0)
            return 0;
        // ssize_t send(int sockfd, const void *buf, size_t len, int flags);
        ssize_t s = send(_sockfd, buf, len, flag);
        if(s < 0)
        {
            if(errno == EAGAIN || errno == EINTR) // 无数据(非阻塞) || 阻塞等待被连接打断
                return 0;
            logMessage(ERROR, "send error, %d:%s", errno, strerror(errno));
            return -1;
        }
        return s;
    }

    ssize_t SendNonBlock(void* buf, size_t len) { return Send(buf, len, MSG_DONTWAIT); }

    bool CreateServer(uint16_t port, const std::string& ip = "0.0.0.0" , bool block = false)
    {
        if(Create() == false)   return false;
        if(block == false)   NonBlock();
        ReuseAddr();
        if(Bind(ip, port) == false) return false;
        if(Listen() == false)  return false;
        return true;
    }

    bool CreateClient(uint16_t port, const std::string& ip)
    {
        if(Create() == false)   return false;
        if(Connect(ip, port) == false) return false;
        return true;
    }

    void Close()
    {
        if(_sockfd >= 0)
        {
            // int close(int fd);
            close(_sockfd);
            _sockfd = -1;
        }
    }

    ~Socket() { Close(); }
private:
    int _sockfd;
};

class NetWork
{
public:
    NetWork()
    {
        signal(SIGPIPE, SIG_IGN);
    }
};

static NetWork nw;